package com.liukaixin.product.ru.data.repositories;

import android.support.annotation.NonNull;
import android.util.Log;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.liukaixin.product.ru.AppController;
import com.liukaixin.product.ru.data.CallBackListener;
import com.liukaixin.product.ru.data.model.Competition;
import com.liukaixin.product.ru.data.model.Result;
import com.liukaixin.product.ru.service.CompetitionService;
import com.liukaixin.product.ru.service.ServiceFactory;
import com.liukaixin.product.ru.util.ACache;
import com.orhanobut.logger.Logger;

import java.util.ArrayList;
import java.util.List;

import rx.Subscriber;
import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers;

import static com.google.common.base.Preconditions.checkNotNull;

/**
 * Concrete implementation to load tasks from the data sources into a cache.
 * <p>
 * For simplicity, this implements a dumb synchronisation between locally persisted data and data
 * obtained from the server, by using the remote data source only if the local database doesn't
 * exist or is empty.
 */
public class CompetitionsRepository {

    private static CompetitionsRepository INSTANCE = null;

    private CompetitionService competitionService;

    private ACache aCache = AppController.getInstance().getCache();

    // Prevent direct instantiation.
    private CompetitionsRepository() {

        this.competitionService = ServiceFactory
                .createRetrofitService(CompetitionService.class,
                        ServiceFactory.BASE_URL);
    }

    /**
     * Returns the single instance of this class, creating it if necessary.
     *
     * @return the {@link CompetitionsRepository} instance
     */
    public static CompetitionsRepository getInstance() {
        if (INSTANCE == null) {
            INSTANCE = new CompetitionsRepository();
        }
        return INSTANCE;
    }

    /**
     * Used to force {@link #getInstance()} to create a new instance
     * next time it's called.
     */
    public static void destroyInstance() {
        INSTANCE = null;
    }

    /**
     * 发布状态的比赛.
     *
     * @param page     页码
     * @param rows     条数
     * @param callback 回调
     */
    public void getReleasedCompetitions(Integer page, Integer rows,
                                        @NonNull
                                        final CallBackListener<List<Competition>> callback) {
        checkNotNull(callback);

        String data = aCache.getAsString("releasedCompetitions" + page + "_" + rows);

        if (data != null && !data.equals("[]")) {
            Logger.d("从缓存中获取第[%d]页的[%d]条竞赛", page, rows);
            callback.onNext(JSON.parseArray(data, Competition.class));
            return;
        }

        competitionService.getReleasedCompetitions(page, rows)
                .subscribeOn(Schedulers.newThread())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Subscriber<Result<List<Competition>>>() {
                    @Override
                    public void onCompleted() {
                        callback.onCompleted();
                    }

                    @Override
                    public void onError(Throwable e) {
                        Logger.e(e, e.getMessage());
                        callback.onError(e.getMessage());
                    }

                    @Override
                    public void onNext(Result<List<Competition>> result) {
                        if (result.getCode().equals(Result.SUCCESS)) {
                            callback.onNext(result.getBean());
                            putInCache("releasedCompetitions" + page + "_" + rows,
                                    JSON.toJSONString(result.getBean()));
                        } else {
                            Logger.e(result.getMessage());
                            callback.onError(result.getMessage());
                        }
                    }
                });
    }

    /**
     * 根据条件获取发布状态打竞赛.
     *
     * @param page     页数
     * @param rows     条数
     * @param type     条件
     * @param callback 回调
     */
    public void getReleasedCompetitionsByType(Integer page,
                                              Integer rows,
                                              String type,
                                              @NonNull
                                              final CallBackListener<
                                                      List<Competition>>
                                                      callback) {
        checkNotNull(callback);
        String data = aCache.getAsString("releasedCompetitionsByConditions" + page + "_" + rows
                + "_" + type);

        if (data != null && !data.equals("[]")) {
            Logger.d("从缓存中获取第[%d]页[%s]类型的[%d]条竞赛", page, type, rows);
            callback.onNext(JSON.parseArray(data, Competition.class));
            return;
        }

        competitionService.getReleasedCompetitionsByType(page, rows, type)
                .subscribeOn(Schedulers.newThread())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Subscriber<Result<List<Competition>>>() {
                    @Override
                    public void onCompleted() {
                        callback.onCompleted();
                    }

                    @Override
                    public void onError(Throwable e) {
                        Logger.e(e, e.getMessage());
                        callback.onError(e.getMessage());
                    }

                    @Override
                    public void onNext(Result<List<Competition>> result) {
                        if (result.getCode().equals(Result.SUCCESS)) {
                            callback.onNext(result.getBean());
                            putInCache("releasedCompetitionsByConditions" + page + "_" + rows
                                    + "_" + type,
                                    JSON.toJSONString(result.getBean()));
                        } else {
                            Logger.e(result.getMessage());
                            callback.onError(result.getMessage());
                        }
                    }
                });
    }

    /**
     * 根据用户id获得竞赛列表
     *
     * @param page     页码
     * @param rows     条数
     * @param userId   用户id
     * @param callback 回调
     */
    public void getCompetitionsByUserId(final Integer page,
                                        final Integer rows,
                                        @NonNull
                                        final Integer userId,
                                        @NonNull
                                        final CallBackListener<List<Competition>> callback) {
        checkNotNull(userId);
        checkNotNull(callback);

        String data = aCache.getAsString("myCompetitions" + page + "_" + rows + "_" + userId);
        if (data != null && !data.equals("[]")) {
            Logger.d("从缓存中获取第[%d]页userId为[%d]的[%d]条竞赛", page, userId, rows);
            callback.onNext(JSON.parseArray(data, Competition.class));
            return;
        }

        competitionService.getCompetitionByUserId(page, rows, userId)
                .subscribeOn(Schedulers.newThread())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Subscriber<Result<List<Competition>>>() {
                    @Override
                    public void onCompleted() {
                        callback.onCompleted();
                    }

                    @Override
                    public void onError(Throwable e) {
                        Logger.e(e, e.getMessage());
                        callback.onError(e.getMessage());
                    }

                    @Override
                    public void onNext(Result<List<Competition>> result) {
                        if (result.getCode().equals(Result.SUCCESS)) {
                            callback.onNext(result.getBean());
                            putInCache("myCompetitions" + page + "_" + rows + "_" + userId,
                                    JSON.toJSONString(result.getBean()));
                        } else {
                            Logger.e(result.getMessage());
                            callback.onError(result.getMessage());
                        }
                    }
                });
    }

    /**
     * 根据竞赛Id获取竞赛详情.
     *
     * @param competitionId 竞赛id
     * @param callback      回调
     */
    public void getCompetitionDescById(Integer competitionId,
                                       @NonNull CallBackListener<Competition> callback) {

        checkNotNull(callback);

        String data = aCache.getAsString("competition desc" + "_" + competitionId);
        if (data != null) {
            Logger.d("从缓存中获取竞赛, id = [%d]", competitionId);
            callback.onNext(JSON.parseObject(data, Competition.class));
            return;
        }

        competitionService.getCompetitionDescById(competitionId)
                .subscribeOn(Schedulers.newThread())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Subscriber<Result<Competition>>() {
                    @Override
                    public void onCompleted() {
                        callback.onCompleted();
                    }

                    @Override
                    public void onError(Throwable e) {
                        Logger.e(e, e.getMessage());
                        callback.onError(e.getMessage());
                    }

                    @Override
                    public void onNext(Result<Competition> result) {
                        if (result.getCode().equals(Result.SUCCESS)) {
                            callback.onNext(result.getBean());
                            putInCache("competition desc" + "_" + competitionId,
                                    JSON.toJSONString(result.getBean()));
                        } else {
                            Logger.e(result.getMessage());
                            callback.onError(result.getMessage());
                        }
                    }
                });
    }

    private void putInCache(String key, String value) {
        aCache.put(key, value, 600);
    }
}
